package com.meego.common.log;

import com.meego.common.config.Config;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;

public class HMTAppender extends AppenderSkeleton {

    private String mTempFileName;
    private int mTempMaxSize;
    private boolean mTempSync;
    private int mTempFlush;
    private String mFileName;
    private int mMaxSize;
    private int mFlush;
    private int mCount;
    private boolean mSync;
    private long mRollover;
    private Writer mFile;
    private int mFileSize;
    private boolean mNoSizeTest;
    private boolean reloading;
    @SuppressWarnings("rawtypes")
    private ArrayList mEventList;
    private Level flushLevel;

    public HMTAppender() {
        this.mFileName = "";
        this.mMaxSize = 0;
        this.mRollover = 0L;
        this.mFile = null;
        this.mFileSize = 0;

        this.mFlush = 80;
        this.mTempFlush = 80;

        this.mNoSizeTest = false;

        this.mSync = true;
        this.mTempSync = true;

        this.reloading = false;
        this.mEventList = null;

        this.flushLevel = getFlushLevel();
    }

    @Override
    public synchronized void activateOptions() {
        this.mFileName = this.mTempFileName;
        this.mMaxSize = this.mTempMaxSize;
        this.mSync = this.mTempSync;
        this.mFlush = this.mTempFlush;
        this.mRollover = 0L;
    }

    public void setFileName(String aFile) {
        this.mTempFileName = aFile;
    }

    public void setSync(boolean aSync) {
        this.mTempSync = aSync;
    }

    public void setFlush(int aFlush) {
        this.mTempFlush = aFlush;
    }

    public void setMaxSize(String aSize) {
        this.mTempMaxSize = 0;

        for (int scan = 0; scan < aSize.length(); ++scan) {
            char c = aSize.charAt(scan);

            if ((c >= '0') && (c <= '9')) {
                this.mTempMaxSize = (this.mTempMaxSize * 10 + c - '0');
            } else {
                if ((c == 'm') || (c == 'M')) {
                    this.mTempMaxSize *= 1048576;
                }
                if ((c != 'k') && (c != 'K')) {
                    return;
                }
                this.mTempMaxSize *= 1024;
                return;
            }
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    protected void append(LoggingEvent loggingEvent) {
        String log = getLayout().format(loggingEvent);
        boolean flush = false;

        Writer tFile;
        synchronized (this) {
            if (this.reloading) {
                if (this.mEventList == null) {
                    this.mEventList = new ArrayList(8);
                }

                this.mEventList.add(log);
                return;
            }

            this.reloading = true;
            reconfigAppender();
            this.reloading = false;

            if (this.mEventList != null) {
                Iterator scan = this.mEventList.iterator();
                while (scan.hasNext()) {
                    myAppend((String) scan.next(), this.mFile, true);
                }
                this.mEventList = null;
            }

            this.mFileSize += log.length();
            this.mCount += 1;
            if (this.mCount >= this.mFlush) {
                this.mCount = 0;
                flush = true;
            } else if ((this.mFlush > 1)
                    && (loggingEvent.getLevel().isGreaterOrEqual(this.flushLevel))) {
                flush = true;
            }

            if (this.mSync) {
                myAppend(log, this.mFile, flush);
                return;
            }
            tFile = this.mFile;
        }

        myAppend(log, tFile, flush);
    }

    private Level getFlushLevel() {
        String level = Config.getStrConfig("meego.log4j.appender.flush_level", "INFO");
        if ("DEBUG".equals(level)) {
            return Level.DEBUG;
        }
        if ("WARN".equals(level)) {
            return Level.WARN;
        }
        if ("ERROR".equals(level)) {
            return Level.ERROR;
        }
        return Level.INFO;
    }

    private void reconfigAppender() {
        long now = System.currentTimeMillis();
        boolean reload = false;

        if ((this.mFileSize >= this.mMaxSize) && (this.mMaxSize > 0) && (!(this.mNoSizeTest))) {
            reload = true;
        }
        if (this.mRollover <= now) {
            reload = true;
        }
        if (reload) {
            if (this.mFile != null) {
                try {
                    this.mFile.close();
                } catch (IOException ex) {
                }
                this.mFile = null;
            }

            Calendar c = Calendar.getInstance();
            c.setTimeInMillis(now);
            c.set(11, 0);
            c.set(12, 0);
            c.set(13, 0);
            c.set(14, 0);
            c.add(5, 1);

            this.mRollover = c.getTimeInMillis();
        }

        if (this.mFile != null) {
            return;
        }

        gaMisc(Level.INFO, "Resetting AMS log4j Appender", null);

        this.mNoSizeTest = false;

        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(now);

        StringBuffer sb = new StringBuffer();

        sb.append(this.mFileName);
        addNumeric(sb, c.get(1), 4);

        switch (c.get(2)) {
            case 0:
                sb.append("01");
                break;
            case 1:
                sb.append("02");
                break;
            case 2:
                sb.append("03");
                break;
            case 3:
                sb.append("04");
                break;
            case 4:
                sb.append("05");
                break;
            case 5:
                sb.append("06");
                break;
            case 6:
                sb.append("07");
                break;
            case 7:
                sb.append("08");
                break;
            case 8:
                sb.append("09");
                break;
            case 9:
                sb.append("10");
                break;
            case 10:
                sb.append("11");
                break;
            case 11:
                sb.append("12");
        }

        addNumeric(sb, c.get(5), 2);

        int sbLength = sb.length();

        int lastExist = -1;
        int lastOK = -1;

        for (int indx = 0; indx <= 100; ++indx) {
            sb.setLength(sbLength);

            if (indx == 100) {
                sb.append("_XX");
            } else {
                sb.append('_');
                addNumeric(sb, indx, 2);
            }
            sb.append(".log");

            File test = new File(sb.toString());

            if (test.exists()) {
                lastExist = indx;
                if (!(test.isFile())) {
                    continue;
                }
                if (!(test.canWrite())) {
                    continue;
                }
                if (!(test.canRead())) {
                    continue;
                }
                if ((test.length() >= this.mMaxSize) && (this.mMaxSize != 0)) {
                    continue;
                }
                lastOK = indx;
            }
        }

        int fileIndx;

        if ((lastOK == lastExist) && (lastOK >= 0)) {
            fileIndx = lastOK;
        } else {
            fileIndx = lastExist + 1;
            if (fileIndx > 100) {
                fileIndx = 100;
            }
        }
        if (fileIndx == 100) {
            this.mNoSizeTest = true;
        }

        sb.setLength(sbLength);
        if (fileIndx == 100) {
            sb.append("_XX");
        } else {
            sb.append('_');
            addNumeric(sb, fileIndx, 2);
        }
        sb.append(".log");
        File test = new File(sb.toString());
        try {
            if (!test.exists()) {
                StringTokenizer tok = new StringTokenizer(sb.toString(), "/");
                String currDir = "";
                while (tok.hasMoreTokens()) {
                    String currDirName = tok.nextToken();
                    if (tok.hasMoreTokens()) {
                        currDir += currDir.isEmpty() ? currDirName : File.separator + currDirName;
                    }

                    new File(currDir).mkdir();
                }
            }
            test.createNewFile();
            this.mFileSize = (int) test.length();
            this.mFile = new BufferedWriter(new FileWriter(sb.toString(), true));

            gaMisc(Level.INFO, "logging to " + sb.toString(), null);
        } catch (FileNotFoundException ex) {
            gaMisc(Level.ERROR, "cannot log to " + sb.toString() + ": " + ex.getMessage(), null);
        } catch (IOException ex) {
            gaMisc(Level.ERROR, "cannot log to " + sb.toString(), ex);
        }
    }

    private void myAppend(String log, Writer aFile, boolean flush) {
        if (aFile == null) {
            System.out.println("AMSAppender:mFile was null: " + log);
        } else {
            try {
                aFile.write(log);
                if (flush) {
                    aFile.flush();
                }
            } catch (IOException ex) {
                System.out.println("AMSAppender:caught IOException: " + log);
            }
        }
    }

    @Override
    public synchronized void close() {
        if (this.mFile == null) {
            return;
        }
        try {
            this.mFile.close();
        } catch (IOException ex) {
        }
        this.mFile = null;
    }

    @Override
    public boolean requiresLayout() {
        return true;
    }

    private void addNumeric(StringBuffer sb, int value, int length) {
        int magnitude = 1;
        for (; length > 1; --length) {
            magnitude *= 10;
        }
        while (true) {
            sb.append((char) (value / magnitude % 10 + 48));
            if (magnitude == 1) {
                return;
            }
            magnitude /= 10;
        }
    }

    private void gaMisc(Level level, Object msg, Throwable th) {
        System.out.print(new HMTLayout().format(new LoggingEvent("org.apache.log4j.Category", GA.misc, level, msg, th)));
    }
}
